home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue50 / Except / LIGen.dpr < prev    next >
Encoding:
Text File  |  1999-09-06  |  19.2 KB  |  653 lines

  1. program LIGen;
  2.  
  3. {$APPTYPE CONSOLE}
  4.  
  5. uses SysUtils, Registry, LIUtils, Windows;
  6.  
  7. type
  8.   TCompileStatus = (csNone, csCompile, csMake, csBuild);
  9.  
  10. var
  11.   ExtraCmdLine:  String = '';
  12.   ProjectName:   String = '';
  13.   OutputDir:     String = '';
  14.   DCC32ExecName: String = 'DCC32.EXE';
  15.   OptFileName:   String;
  16.   MapFileName:   String;
  17.   CfgFileName:   String;
  18.   UseDCCVer:     String;
  19.   CompileStatus: TCompileStatus = csNone;
  20.   OverwriteCfg:  Boolean = False;
  21.   PauseOnError:  Boolean = False;
  22.   DCC32CfgOnly:  Boolean = False;
  23.   DontGenerateDCC32Cfg: Boolean = False;
  24.   CfgFile:       TextFile;
  25.   UnitTable:     TGrowingArray;
  26.   PublicList:    TGrowingArray;
  27.   LineNumbers:   TGrowingArray;
  28.   Resource:      TGrowingArray;
  29.   RTLIHeader:    TRTLIHeader;
  30.  
  31. // Displays command line syntax and terminates
  32.  
  33. procedure DisplaySyntax;
  34. begin
  35.   WriteLn('Syntax: LIGen [Options] ProjectFile');
  36.   WriteLn('/N           create DCC32.CFG only, do Not compile and generate RTLI');
  37.   WriteLn('/O           Overwrite DCC32.CFG');
  38.   WriteLn('/P           Pause on error: wait for the Enter key to be pressed');
  39.   WriteLn('/Rb,/Rc,/Rm  Run DCC32 only: Rb=build, Rc=compile, Rm=make project');
  40.   WriteLn('/S<x>        pass command line Switch -<x> directly to DCC32');
  41.   WriteLn('/V<X.0>      Use DCC32 for version X.0 of Delphi');
  42.   WriteLn('/?,/H        display this Help screen');
  43.   Halt(1);
  44. end;
  45.  
  46. // Displays an error message and terminates
  47.  
  48. procedure Error(const ErrStr: String; const Params: array of const);
  49. begin
  50.   WriteLn('**Error**  ', Format(ErrStr, Params));
  51.   if PauseOnError then
  52.   begin
  53.     WriteLn('Press Enter to exit');
  54.     ReadLn;
  55.   end;
  56.   Halt(2);
  57. end;
  58.  
  59. // Reports an invalid command line option error
  60.  
  61. procedure InvalidCmdLineOption(const ParmStr: String);
  62. begin
  63.   Error('Invalid command line option "%s"', [ParmStr]);
  64. end;
  65.  
  66. // Reports an unsuccessful compilation
  67.  
  68. procedure CompilationFailed(ExitCode: Integer);
  69. begin
  70.   Error('Compilation failed, return code = %d', [ExitCode]);
  71. end;
  72.  
  73. // Parses the supplied command line
  74.  
  75. procedure ParseCmdLine;
  76. var
  77.   ParmIndex: Integer;
  78.   ParmStr: String;
  79. begin
  80.   ParmIndex := 1;
  81.   ParmStr := '';
  82.   repeat
  83.     ParmStr := ParamStr(ParmIndex);
  84.     Inc(ParmIndex);
  85.     if ParamCount = 0 then ParmStr := '/?';
  86.     if (ParmStr <> '') then
  87.     begin
  88.       if not (ParmStr[1] in ['-', '/']) then
  89.         ProjectName := ExpandFileName(ParmStr)
  90.       else
  91.         case Length(ParmStr) of
  92.           2:
  93.             case UpCase(ParmStr[2]) of
  94.               'D': DontGenerateDCC32Cfg := True;
  95.               'N': DCC32CfgOnly  := True;
  96.               'O': OverwriteCfg := True;
  97.               'P': PauseOnError := True;
  98.               '?','H': DisplaySyntax;
  99.               else InvalidCmdLineOption(ParmStr);
  100.             end;
  101.           3..1024:
  102.             case UpCase(ParmStr[2]) of
  103.               'S':
  104.                 begin
  105.                   if ExtraCmdLine <> '' then
  106.                     ExtraCmdLine := ExtraCmdLine + ' ';
  107.                   ExtraCmdLine := ExtraCmdLine + '-' + Copy(ParmStr, 3, MaxInt);
  108.                 end;
  109.               'V':
  110.                 begin
  111.                   UseDCCVer := Copy(ParmStr, 3, 3);
  112.                 end;
  113.               'R':
  114.                 begin
  115.                   if (Length(ParmStr) <> 3) or not (UpCase(ParmStr[3]) in ['B','C','M']) then
  116.                     InvalidCmdLineOption(ParmStr);
  117.                   case UpCase(ParmStr[3]) of
  118.                     'B': CompileStatus := csBuild;
  119.                     'C': CompileStatus := csCompile;
  120.                     'M': CompileStatus := csMake;
  121.                   end;
  122.                 end;
  123.               else InvalidCmdLineOption(ParmStr);
  124.             end;
  125.         end;
  126.     end;
  127.   until ParmStr = '';
  128.   if ProjectName = '' then
  129.     DisplaySyntax;
  130. end;
  131.  
  132. // The following three functions are similar to the corresponding Dos unit
  133. // functions found in Borland Pascal. Unfortunately, Dos unit disappeared
  134. // in Delphi and there are no equivalent functions in the SysUtils unit.
  135. // So we have to implement them here.
  136.  
  137. var
  138.   ProcessInfo: TProcessInformation;
  139.  
  140. function Exec(const Path,CmdLine: String): Integer;
  141. var
  142.   Win32Path: String;
  143.   Win32CmdLine: String;
  144.   StartupInfo: TStartupInfo;
  145. begin
  146.   Win32Path := ExpandFileName(Path);
  147.   if Win32Path <> '' then
  148.     if Win32Path[1] <> '"' then
  149.       Win32Path := '"' + Win32Path + '"';
  150.   Win32CmdLine := Win32Path + ' ' + CmdLine;
  151.   FillChar(StartupInfo, SizeOf(TStartupInfo), 0);
  152.   with StartupInfo do
  153.   begin
  154.     cb := SizeOf(TStartupInfo);
  155.     dwFlags := startf_UseShowWindow;
  156.     wShowWindow := sw_ShowNormal;
  157.   end;
  158.   if CreateProcess(nil, PChar(Win32CmdLine), nil, nil, True, normal_Priority_Class, nil, nil, StartupInfo, ProcessInfo) then
  159.     begin
  160.       WaitForSingleObject(ProcessInfo.hProcess, Infinite);
  161.       Result := 0;
  162.     end
  163.   else
  164.     Result := GetLastError;
  165. end;
  166.  
  167. function DosExitCode: DWord;
  168. begin
  169.   GetExitCodeProcess(ProcessInfo.hProcess, Result);
  170. end;
  171.  
  172. function GetEnv(const EnvVar: String): String;
  173. var
  174.   Buffer: array[0..1023] of Char;
  175. begin
  176.   SetString(Result, Buffer,
  177.     GetEnvironmentVariable(PChar(EnvVar), Buffer, SizeOf(Buffer)));
  178. end;
  179.  
  180. // Reads an integer value from the INI file
  181.  
  182. function OptReadInteger(const AppName,KeyName: String; Default: Integer): Integer;
  183. begin
  184.   Result := GetPrivateProfileInt(PChar(AppName), PChar(KeyName), Default,
  185.     PChar(OptFileName));
  186. end;
  187.  
  188. // Reads a string value from the INI file
  189.  
  190. function OptReadString(const AppName,KeyName,Default: String): String;
  191. var
  192.   Buffer: array[0..259] of Char;
  193. begin
  194.   GetPrivateProfileString(PChar(AppName), PChar(KeyName), PChar(Default),
  195.     Buffer, SizeOf(Buffer), PChar(OptFileName));
  196.   Result := Buffer;
  197. end;
  198.  
  199. procedure AppendToCmdLine(const S: String);
  200. var
  201.   C: Char;
  202. begin
  203.   if S = '(' then
  204.     begin
  205.       CfgFileName := ExtractFilePath(OptFileName) + '\DCC32.CFG';
  206.       C := 'Y';
  207.       if FileExists(CfgFileName) then
  208.       begin
  209.         if not OverwriteCfg then
  210.         begin
  211.           Write('File DCC32.CFG already exists. Overwrite? (Y/N)');
  212.           repeat
  213.             ReadLn(C);
  214.           until C in ['Y', 'N', 'y', 'n'];
  215.         end;
  216.       end;
  217.       if UpCase(C) = 'N' then
  218.         Halt(3);
  219.       AssignFile(CfgFile, CfgFileName);
  220.       Rewrite(CfgFile);
  221.       if IOResult <> 0 then
  222.         Error('Cannot create CFG file "%s"', [CfgFileName]);
  223.     end
  224.   else
  225.     if S = ')' then
  226.       begin
  227.         CloseFile(CfgFile);
  228.         IOResult; //  := 0;
  229.       end
  230.     else
  231.       begin
  232.         WriteLn(CfgFile, S);
  233.         if IOResult <> 0 then
  234.           Error('Error writing file "%s" - %s', [CfgFileName, SysErrorMessage(IOResult)]);
  235.       end;
  236. end;
  237.  
  238. // Forms the command line compiler configuration file based on the settings
  239. // found in the project option file
  240.  
  241. procedure FormDCC32Config;
  242. var
  243.   C,State: Char;
  244.   Value: Integer;
  245.   CfgStr: String;
  246. begin
  247.   OptFileName := ChangeFileExt(ProjectName, '.DOF');
  248.   if not FileExists(OptFileName) then
  249.     Error('Cannot find project option file "%s"', [OptFileName]);
  250.   CfgStr := '';
  251.   for C := 'A' to 'Z' do
  252.   begin
  253.     Value := OptReadInteger('Compiler', C, 2);
  254.     case Value of
  255.       0: State := '-';
  256.       1: State := '+';
  257.       else
  258.         if C = 'A' then
  259.           Error('Invalid Delphi options file "%s"', [OptFileName]);
  260.         State := '-';
  261.     end;
  262.     CfgStr := Format('%s-$%s%s ', [CfgStr, C, State]);
  263.   end;
  264.   AppendToCmdLine('(');
  265.   AppendToCmdLine(CfgStr);
  266.   case CompileStatus of
  267.     csMake:  AppendToCmdLine('-M');
  268.     csBuild: AppendToCmdLine('-B');
  269.   end;
  270.   if OptReadInteger('Compiler', 'ShowHints', 2) = 1 then
  271.     AppendToCmdLine('-H');
  272.   if OptReadInteger('Compiler', 'ShowWarnings', 2) = 1 then
  273.     AppendToCmdLine('-W');
  274.   CfgStr := OptReadString('Compiler', 'UnitAliases', '');
  275.   if CfgStr <> '' then
  276.     AppendToCmdLine('-A' + CfgStr);
  277.   OutputDir := OptReadString('Directories', 'OutputDir', '');
  278.   if OutputDir <> '' then
  279.     AppendToCmdLine('-E"' + OutputDir + '"');
  280.   CfgStr := OptReadString('Directories', 'SearchPath', '');
  281.   if CfgStr <> '' then
  282.   begin
  283.     AppendToCmdLine('-U"' + CfgStr + '"');
  284.     AppendToCmdLine('-I"' + CfgStr + '"');
  285.     AppendToCmdLine('-R"' + CfgStr + '"');
  286.     AppendToCmdLine('-O"' + CfgStr + '"');
  287.   end;
  288.   CfgStr := OptReadString('Directories', 'Conditionals', '');
  289.   if CfgStr <> '' then
  290.     AppendToCmdLine('-D' + CfgStr);
  291.   C := #0;
  292.   case OptReadInteger('Linker', 'MapFile', 0) of
  293.     1: C := 'S';
  294.     2: C := 'P';
  295.     3: C := 'D';
  296.   end;
  297.   if C <> #0 then
  298.     AppendToCmdLine('-G' + C);
  299.   if OptReadInteger('Linker', 'OutputObjs', 0) <> 0 then
  300.     AppendToCmdLine('-J');
  301.   C := 'C';
  302.   if OptReadInteger('Linker', 'ConsoleApp', 0) = 1 then
  303.     C := 'G';
  304.   AppendToCmdLine('-C' + C);
  305.   if OptReadInteger('Linker', 'DebugInfo', 0) <> 0 then
  306.     AppendToCmdLine('-V');
  307.   AppendToCmdLine(Format('-M%d,%d',
  308.     [OptReadInteger('Linker', 'MinStackSize', 16384),
  309.      OptReadInteger('Linker', 'MaxStackSize', 1048576)]));
  310.   AppendToCmdLine(Format('-K%x', [OptReadInteger('Linker', 'ImageBase', $400000)]));
  311.   AppendToCmdLine(')');
  312. end;
  313.  
  314.  
  315. // Runs the command line compiler
  316. function CompileProject(const CtrlParam: String): Integer;
  317. var
  318.   ErrCode: Integer;
  319.   DCCName: String;
  320.   Registry: TRegistry;
  321.  
  322.   function HasPathForVersion(const Version: string): boolean;
  323.   begin
  324.     Result := Registry.OpenKey('\SOFTWARE\Borland\Delphi\'+Version, False);
  325.     if Result then
  326.     begin
  327.       DCCName := Registry.ReadString('RootDir');
  328.       if DCCName <> '' then
  329.         DCCName := DCCName + '\BIN\DCC32.EXE';
  330.     end;
  331.   end;
  332.  
  333. begin
  334.   Registry := TRegistry.Create;
  335.   Registry.RootKey := hkey_Local_Machine;
  336.   try
  337.     // Has the user specificed the compiler version?
  338.     if UseDCCVer <> '' then
  339.       HasPathForVersion(UseDCCVer)
  340.     else
  341.     begin
  342.       // Try to find the command line compiler executable
  343.       DCCName := FileSearch(DCC32ExecName, GetEnv('PATH'));
  344.       if DCCName <> '' then
  345.         DCCName := ExpandFileName(DCCName)
  346.       else
  347.       begin
  348.         // Executable is not found in 'PATH'. Try to find the Delphi directory
  349.         // setting RootDir in the Registry under
  350.         // HKEY_LOCAL_MACHINE\SOFTWARE\Borland\Delphi\X.0
  351.         if HasPathForVersion('5.0') or
  352.            HasPathForVersion('4.0') or
  353.            HasPathForVersion('3.0') or
  354.            HasPathForVersion('2.0') then
  355.           ;
  356.       end;
  357.     end;
  358.   finally
  359.     Registry.Destroy; // Hmm, looks a bit severe :-)
  360.   end;
  361.   if DCCName = '' then
  362.     Error('Cannot find file %s, make sure it is included in PATH, or use the /V option', [DCC32ExecName]);
  363.   // Change to the directory where the project is located
  364.   SetCurrentDir(ExtractFilePath(OptFileName));
  365.   // Run the command line compiler
  366.   ErrCode := Exec(DCCName, ChangeFileExt(ProjectName, '.DPR') + ' ' + ExtraCmdLine + CtrlParam);
  367.   if ErrCode <> 0 then
  368.     Error('Cannot execute %s - %s', [DCCName, SysErrorMessage(ErrCode)]);
  369.   Result := DosExitCode;
  370. end;
  371.  
  372. // Figures out the name of the .MAP file
  373.  
  374. procedure GetMapFileName;
  375. begin
  376.   if OutputDir = '' then
  377.     MapFileName := ProjectName
  378.   else
  379.     begin
  380.       MapFileName := ExtractFileName(ProjectName);
  381.       if OutputDir[Length(OutputDir)] <> '\' then
  382.         MapFileName := OutputDir + '\' + MapFileName
  383.       else
  384.         MapFileName := OutputDir + MapFileName;
  385.     end;
  386.   MapFileName := ChangeFileExt(MapFileName, '.MAP');
  387. end;
  388.  
  389. // Parses a map file
  390.  
  391. procedure ParseMapFile;
  392. var
  393.   C: Char;
  394.   I,MapLineNo,SegNo,LnNo,LnOfs,LastLnNo,LastOfs,CurOfs,CodeEnd: Integer;
  395.   Buffer: array[0..299] of Char;
  396.   S,Name,SrcName: String;
  397.   MapFile: Text;
  398.  
  399. procedure InvalidMapFile;
  400. begin
  401.   Error('Invalid format of the map file "%s" at line %d', [MapFileName, MapLineNo]);
  402. end;
  403.  
  404. procedure ReadMapLine;
  405. begin
  406.   ReadLn(MapFile, S);
  407.   Inc(MapLineNo);
  408.   if IOResult <> 0 then
  409.     Error('Error reading map file "%s" - %s', [MapFileName, SysErrorMessage(IOResult)]);
  410. end;
  411.  
  412. procedure WriteData(const A: TGrowingArray; Size: Integer);
  413. begin
  414.   Move(Buffer, A.Allocate(Size)^, Size);
  415. end;
  416.  
  417. begin
  418.   UnitTable   := TGrowingArray.Create(512, 512, 1);
  419.   PublicList  := TGrowingArray.Create(8*1024, 8*1024, 1);
  420.   LineNumbers := TGrowingArray.Create(8*1024, 8*1024, 1);
  421.   Resource    := TGrowingArray.Create(16*1024, 16*1024, 1);
  422.   FillChar(RTLIHeader, SizeOf(RTLIHeader), 0);
  423.   Assign(MapFile, MapFileName);
  424.   Reset(MapFile);
  425.   if IOResult <> 0 then
  426.     Error('Cannot open file "%s" - %s', [MapFileName, SysErrorMessage(IOResult)]);
  427.   // Parse detailed segment map, for example:
  428.   // 0001:00000000 00000B90 C=CODE     S=.text    G=(none)   M=System   ACBP=A9
  429.   // 0001:00000B90 00000019 C=CODE     S=.text    G=(none)   M=PROGRAM  ACBP=A9
  430.   MapLineNo := 0;
  431.   while not EOF(MapFile) do
  432.   begin
  433.     ReadMapLine;
  434.     if S = 'Detailed map of segments' then
  435.       Break;
  436.   end;
  437.   if EOF(MapFile) then
  438.     InvalidMapFile;
  439.   ReadMapLine;
  440.   CodeEnd := 0;
  441.   repeat
  442.     ReadMapLine;
  443.     if S <> '' then
  444.     begin
  445.       I := 1;
  446.       SkipBlanks(S, I);
  447.       SegNo := ParseHex(S, I);
  448.       if SegNo > 1 then
  449.         Break;
  450.       C := ParseChr(S, I);
  451.       CurOfs := ParseHex(S, I);
  452.       SkipBlanks(S, I);
  453.       CodeEnd := ParseHex(S, I) + CurOfs;
  454.       I := Pos('M=', S);
  455.       Name := '';
  456.       if I > 0 then
  457.       begin
  458.         Inc(I, 2);
  459.         Name := ParseStr(S, I);
  460.       end;
  461.       if (C <> ':') or (SegNo = -1) or (CurOfs = -1) or (Name = '') then
  462.         InvalidMapFile;
  463.       PDWord(@Buffer)^ := CurOfs;
  464.       WriteData(UnitTable, SizeOf(DWord));
  465.       WriteData(UnitTable, EncodeString(Name, Buffer));
  466.       Inc(RTLIHeader.rtliUnitCount);
  467.     end;
  468.   until S = '';
  469.   // Ending code offset
  470.   PDWord(@Buffer)^ := CodeEnd;
  471.   WriteData(UnitTable, SizeOf(DWord));
  472.   // Parse public table, for example
  473.   // 0001:00000000       TextStart
  474.   // 0001:00000234       @HandleFinally
  475.   // 0001:0000026C       @SafeCall
  476.   while not EOF(MapFile) do
  477.   begin
  478.     ReadMapLine;
  479.     if Pos('Publics by Value', S) <> 0 then
  480.     begin
  481.       ReadMapLine;
  482.       Break;
  483.     end;
  484.   end;
  485.   LastOfs := 0;
  486.   if EOF(MapFile) then
  487.     InvalidMapFile;
  488.   repeat
  489.     ReadMapLine;
  490.     I := 1;
  491.     SkipBlanks(S, I);
  492.     SegNo := ParseHex(S, I);
  493.     if SegNo = 1 then
  494.     begin
  495.       C := ParseChr(S, I);
  496.       CurOfs := ParseHex(S, I);
  497.       SkipBlanks(S, I);
  498.       Name := ParseStr(S, I);
  499.       if (C <> ':') or (CurOfs = -1) or (Name = '') then
  500.         InvalidMapFile;
  501.       WriteData(PublicList, EncodeString(Name, Buffer));
  502.       WriteData(PublicList, EncodeSymbolOfs(Buffer, CurOfs - LastOfs));
  503. {      if Name = '___Fixup___' then
  504.         RTLIHeader.rtliFixup := CurOfs;}
  505.       Inc(RTLIHeader.rtliPublicCount);
  506.       LastOfs := CurOfs;
  507.     end;
  508.   until S = '';
  509.   // Terminating entry
  510.   Buffer[0] := #0;
  511.   WriteData(PublicList, 1);
  512.   WriteData(PublicList, EncodeSymbolOfs(Buffer, CodeEnd - LastOfs));
  513.   // Parse line number information, for example
  514.   // Line numbers for MyProg(myprog.pas) segment .text
  515.   //
  516.   //   1 0001:00000B90     2 0001:00000BA0
  517.   while not EOF(MapFile) do
  518.   begin
  519.     ReadMapLine;
  520.     I := Pos('Line numbers for', S);
  521.     if I <> 0 then
  522.     begin
  523.       Inc(I, 16);
  524.       SkipBlanks(S, I);
  525.       Name := ParseStr(S, I);
  526.       C := ParseChr(S, I);
  527.       SrcName := ParseStr(S, I);
  528.       if (Name = '') or (SrcName = '') or (C <> '(') then
  529.         InvalidMapFile;
  530.       Name := SrcName;
  531.       Buffer[0] := Chr(escFileName);
  532.       WriteData(LineNumbers, EncodeString(Name, @Buffer[1]) + 1);
  533.       ReadMapLine; // Skip blank line
  534.       ReadMapLine;
  535.       LastOfs := 0;
  536.       LastLnNo := 0;
  537.       repeat
  538.         I := 1;
  539.         repeat
  540.           SkipBlanks(S, I);
  541.           LnNo := ParseDec(S, I);
  542.           SkipBlanks(S, I);
  543.           SegNo := ParseHex(S, I);
  544.           C     := ParseChr(S, I);
  545.           LnOfs := ParseHex(S, I);
  546.           if (SegNo <> 1) or (C <> ':') or (LnOfs = -1) then
  547.             InvalidMapFile;
  548.           WriteData(LineNumbers, EncodeLineNumber(Buffer, LnNo - LastLnNo, LnOfs - LastOfs));
  549.           LastLnNo := LnNo;
  550.           LastOfs := LnOfs;
  551.           Inc(RTLIHeader.rtliLineCount);
  552.           SkipBlanks(S, I);
  553.         until I > Length(S);
  554.         ReadMapLine;
  555.       until S = '';
  556.     end;
  557.   end;
  558.   CloseFile(MapFile);
  559.   IOResult; // := 0;
  560. //  if RTLIHeader.rtliFixup = 0 then
  561. //    Error('RTLI is not used in the project %s', [ProjectName]);
  562. end;
  563.  
  564. procedure FormResourceFile;
  565. const
  566.   Signature: array[0..31] of Byte =
  567.     ($00,$00,$00,$00,$20,$00,$00,$00,$FF,$FF,$00,$00,$FF,$FF,$00,$00,
  568.      $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00);
  569.    ResHdr: array [0..31] of Byte =
  570.      ($00,$00,$00,$00,$20,$00,$00,$00,$FF,$FF,$0A,$00,$FF,$FF,$77,$77,
  571.       $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00);
  572.    PadBytes: array[0..2] of Byte = (0, 0, 0);
  573. var
  574.   ResSizeOfs: Integer;
  575.  
  576. procedure WriteToResource(const Buffer; Size: Integer);
  577. begin
  578.   Move(Buffer, Resource.Allocate(Size)^, Size);
  579. end;
  580.  
  581. begin
  582.   // Record identifing the resource file as a file containing 32-bit resources
  583.   WriteToResource(Signature, SizeOf(Signature));
  584.   // Resource header
  585.   ResSizeOfs := Resource.Count;
  586.   WriteToResource(ResHdr, SizeOf(ResHdr));
  587.   // Resource itself:
  588.   // RTLI Header
  589.   WriteToResource(RTLIHeader, SizeOf(RTLIHeader));
  590.   //  - Unit table
  591.   WriteToResource(UnitTable.ArrPtr^, UnitTable.Count);
  592.   UnitTable.Destroy;
  593.   //  - Public Table
  594.   WriteToResource(PublicList.ArrPtr^, PublicList.Count);
  595.   PublicList.Destroy;
  596.   //  - Line number information
  597.   WriteToResource(LineNumbers.ArrPtr^, LineNumbers.Count);
  598.   LineNumbers.Destroy;
  599.   // Align resource at DWord boundary
  600.   if (Resource.Count and $3) <> 0 then
  601.     WriteToResource(PadBytes, 4 - (Resource.Count and $3));
  602.   PDWord(PChar(Resource.ArrPtr) + ResSizeOfs)^ := Resource.Count - ResSizeOfs - SizeOf(ResHdr);
  603. end;
  604.  
  605. procedure StoreResourceFile;
  606. var
  607.   ResFileName: AnsiString;
  608.   ResFile: file;
  609. begin
  610.   ResFileName := ChangeFileExt(ProjectName, '.RLI');
  611.   Assign(ResFile, ResFileName);
  612.   Rewrite(ResFile, 1);
  613.   if IOResult <> 0 then
  614.     Error('Cannot create file "%s" - %s', [ResFileName, SysErrorMessage(IOResult)]);
  615.   BlockWrite(ResFile, Resource.ArrPtr^, Resource.Count);
  616.   if IOResult <> 0 then
  617.     Error('Error writing file "%s" - %s', [ResFileName, SysErrorMessage(IOResult)]);
  618.   Close(ResFile);
  619.   IOResult; // := 0;
  620.   Resource.Destroy;
  621. end;
  622.  
  623. var
  624.   ExitCode: Integer;
  625.  
  626. begin
  627.   WriteLn('RTLI Generator/DCC32 launcher for Delphi2  Version 1.0');
  628.   ParseCmdLine;
  629.   if not DontGenerateDCC32Cfg then
  630.     FormDCC32Config;
  631.   if not DCC32CfgOnly then
  632.   begin
  633.     if CompileStatus <> csNone then
  634.       // Terminate itself passing the exit code from the compiler.
  635.       // This ensures that MAKE process fails if the compilation is unsuccessful
  636.       Halt(CompileProject(''))
  637.     else
  638.       begin
  639.         CompileStatus := csMake;
  640.         ExitCode := CompileProject(' -GD -M');
  641.         if ExitCode <> 0 then
  642.           CompilationFailed(ExitCode);
  643.         GetMapFileName;
  644.         ParseMapFile;
  645.         FormResourceFile;
  646.         StoreResourceFile;
  647.         ExitCode := CompileProject(' -DBindingRTLI');
  648.         if ExitCode <> 0 then
  649.           CompilationFailed(ExitCode);
  650.       end;
  651.   end;
  652. end.
  653.